home *** CD-ROM | disk | FTP | other *** search
/ Gekikoh Dennoh Club 1 / Gekikoh Dennoh Club Vol. 1 (Japan).7z / Gekikoh Dennoh Club Vol. 1 (Japan) (Track 1).bin / kowin / archive / sys / kowin14d.lzh / doc / programming / kowin_p7.doc < prev    next >
Text File  |  1995-09-15  |  8KB  |  227 lines

  1.  
  2. Ko-Window プログラミング、入門編 その5  「デバッグのために」
  3.  
  4.  
  5.  
  6.   プログラミングとは切っても切れない間柄のデバッグ作業。それについてもここで
  7. 少し触れてみましょう。
  8.  
  9.  
  10. ●コンパイルエラーとバグ
  11.  
  12.   コンパイル時に出てくるエラーはバグ以前の問題で、いわゆる文法エラーです。こ
  13. れは慣れた人なら、メッセージが出た瞬間その原因がひらめき、ささっとソースを修
  14. 正して直してしまうことでしょう。エラーメッセージが流れるほど出てくることもあ
  15. りますが、いちいち全部追い掛ける必要はありません。たいてい、最初の文法エラー
  16. が原因でその後のコンパイルに影響を与えてるだけなので、最初の要因を直してから、
  17. コンパイル、を繰り返せば途端にエラーが減っていくはずです。
  18.  
  19.   ただソースリストが大きかったりすると、何度もコンパイルしたりエディタで編集
  20. したりするのは時間もかかり、結構やっかいです。ところがこの作業は、工夫しだい
  21. でかなり効率良くデバッグできるようになります。それが分割コンパイルです。
  22.  
  23.  
  24.  
  25. ●分割コンパイル
  26.  
  27.   分割して1つ1つのソースを小さくすることは、開発に対してさまざまな利点をもた
  28. らします。
  29.  
  30.   ・エラー箇所が分かりやすくなる
  31.  
  32.     分割して関連ある部分ごとまとソースにめてあるため、エラー箇所の絞り込
  33.     みが容易で修正しやすくなります。またグローバル参照の変数や関数を減ら
  34.     し、個々に閉じたプログラムを書くことができるため、シンボル衝突を防ぐ
  35.     と同時に保守性を高める効果もあります。
  36.  
  37.   ・コンパイル時間が短縮される
  38.  
  39.     これは分割における最大のメリットの1つです。デバッグによる修正も書き換
  40.     えたソースのみ再コンパイルすれば済むので、コンパイル時間は大幅に減少
  41.     されます。1番時間のかかるコンパイル処理が、だいたい 1/分割数 で済むわ
  42.     けですから、どれだけ効果が大きいかわかるでしょう。
  43.  
  44.   ・消費メモリが少なくなる
  45.  
  46.     一度にコンパイルする容量が小さくなるため、コンパイラの消費するメモリ
  47.     が小さくなります。だいたいフリーエリアが 1.5M あれば、make 上で余裕
  48.     でコンパイルできるはずです。メモリの少ないマシンではもちろん、メモリ
  49.     をかなり増設しているマシンでも、これで浮いたメモリをディスクキャッシュ
  50.     や RAMDISK に回せば、ますます速いコンパイルができるようになるでしょう。
  51.  
  52.   このように、徹底した分割コンパイルは、開発効率を最大限あげることにつながり
  53. ます。
  54.  
  55.   もちろん make は必須のコマンドです。これ無くして分割コンパイルはできません。
  56.  gnumake もしくは XC v2 付属の make が使えます。(gnumake を使う場合は消費メ
  57. モリが増えるので、メモリのフリーエリアに注意して下さい)
  58.  
  59.   経験上、X68000 では1つのソースは大体 2~3Kbyte、どんなに大きくなっても
  60.  10Kbyte を越えてはいけません。私が今まで書いてきた厖大な量の Ko-Window アプ
  61. リケーションが、極めて短期間の間に組まれていることを見れば、分割開発にどれだ
  62. け意義があるのかわかるのではないでしょうか。もちろん当初使っていたマシンは
  63.  10MHz 無改造の X68000 です。初期の厖大なプログラムは全部マシンで組みました。
  64.  
  65.  
  66.  
  67. ●やっかいなバグとは
  68.  
  69.   本当にやっかいなバグというのは、コンパイルが問題なく通った後にはじめて現れ
  70. ます。いきなりバスエラーで止まるもの、起動したものの思い通りに動かないもの、
  71. そして1番難解なものは、ちゃんと動くようにみえるのに何度もやってるとたまに失
  72. 敗する、というものです。
  73.  
  74.   デバッグに必要な情報は2つ、
  75.  
  76.     バグのある場所を特定する
  77.     バグの原因を究明する
  78.  
  79. デバッグの難しさは、この2つがどれだけ絞り込めるかに大きく依存しています。
  80.  
  81.   デバッグにどこから手をつけていいかわからないという場合は、まずこのバグのあ
  82. る場所の場所を限定することから手をつけるのが得策です。
  83.  
  84.  
  85.  
  86. ●イベントスタック表示を見る
  87.  
  88.   Ko-Window でシステムエラーが発生した場合、エラーウィンドウにその時のイベン
  89. トスタックのダンプ表示を行わせることができます。これを見ておくと、だいたいど
  90. のイベント発生時に起こったものか、というのを判断することができます。
  91.   これは wsrv.x の起動時にオプションで指定します。詳しくは基本セット付属の
  92.  wsrv.doc を参照して下さい。
  93.  
  94.     wsrv.x -d1        エラー時にイベントスタックの表示を行う
  95.     wsrv.x -d3        デバッグモードで起動する
  96.     wsrv.x -d7        トレースモードで起動する
  97.  
  98.  
  99.  
  100. ●プログラムが思いどおりに動かない場合、どうやってデバッグをしますか?
  101.  
  102.   私の場合はまず、怪しいと思われるコード部分とじーっと睨めっこします。もちろん
  103. 頭の中でプログラムのトレースをしているわけです。
  104.  
  105.   バグの発生しやすい場所は、アドレス(ポインタ)、スタック、ファイル、です。も
  106. しバスエラーやアドレスエラーが発生する場合であれば、原因はこの中に収まる可能
  107. 性が極めて大です。
  108.  
  109.  
  110.   それで原因がわからなかった時は、エラー箇所の絞り込み、を行います。少なくと
  111. もどこまではプログラムがちゃんと動いたか、またはその時の変数の値でもわかれば、
  112. 要因を探る材料にもなります。
  113.  
  114.   コードの節目に printf() 等を徹底して埋め込み、場合によっては変数値を表示さ
  115. せながら動かし、場所の特定を行います。ただし、この printf() というのはウィン
  116. ドウプログラムでは使えません(標準入出力がないためです)。そこで代わりに Console
  117. というのを用います。
  118.  
  119.  
  120.  
  121. ● Console への出力
  122.  
  123.   ウィンドウ画面では、同時に複数のプログラムが動いているためどこに文字列を表
  124. 示する、という特定ができません。そこでどれか1つのウィンドウを代表者と決め、
  125. ちょっとしたエラーメッセージ等の文字列表示をすべてそこに行わせることにします。
  126. その代表者を Console といいます。
  127.  
  128.   現在 Console として使えるプログラムは、Command.win と KoConsole.win の2つで
  129. す。Command.win は、-tConsole というオプションをつけたウィンドウだけが Console
  130. になります。KoConsole.win は Console の出力表示専用です。もちろん、Console は
  131. ウィンドウ上に 1つしか存在できません。
  132.  
  133.   Console へ文字列を表示させるのは簡単です。ウィンドウプログラムの好きな場所
  134. に、次のような行を加えてみて下さい。
  135.  
  136.     ConsoleOpen();
  137.     ConsolePrint( "Consoleへの文字列表示\r\n" );
  138.  
  139.  Close は必要ありません。また ConsoleOpen() は何度実行しても大丈夫です。とに
  140. かく ConsolePrint() の前に1回以上 ConsoleOpen() を書いておけば正しく Console
  141. に出力が行われるはずです。
  142.  
  143.   printf() のようなフォーマット付き出力を行う場合は、ConsolePrintf() を使いま
  144. す。例えば
  145.  
  146.     ConsoleOpen();
  147.     ConsolePrintf( "i=%d\r\n", i );
  148.  
  149. となります。
  150.  
  151.   Console について詳しくは、別ドキュメントで触れたいと思います。ここではデバッ
  152. グ時に活用できる、ということで覚えておいて下さい。
  153.  
  154.  
  155.  
  156. ● Ko-Window でひっかかりやすいポイント
  157.  
  158.   ありがち用例集
  159.  
  160.  
  161. ・fopen() が失敗する
  162.  
  163.     fopen() が原因不明で失敗する、などという場合は HEAP エリアの確保をし
  164.     ていない可能性があります。グローバル変数 WindowHeapSize の設定を行っ
  165.     ているかどうか再確認してみて下さい。 -> file.doc 参照
  166.  
  167.  
  168. ・表示文字列が崩れる
  169.  
  170.     DrawSet~() の関数は、実際の表示は行わずにワークエリアに情報を格納す
  171.     るだけに過ぎません。表示そのものは WindowDraw() によってはじめて行わ
  172.     れるため、それを忘れているとうっかり次のようなプログラムを書いてしま
  173.     うことがあります。
  174.  
  175.         drawset( dbuf, num1, num2 )
  176.         DrawBuf    *dbuf;
  177.         {
  178.             char    buf[100];
  179.             sprintf( buf, "%d", num1 );
  180.             DrawSetSymbol( dbuf,   X1, Y1, buf, ATTR, FONT );
  181.             sprintf( buf, "%d", num2 );
  182.             DrawSetSymbol( dbuf+1, X2, Y2, buf, ATTR, FONT );
  183.         }
  184.  
  185.     これはどこが悪いかわかりますでしょうか。まずい点は2つあります。
  186.  
  187.     1) 文字列領域 buf が重なっている
  188.  
  189.         これを実際に動かすと、num2 の値が2つ表示されてしまうことでしょ
  190.         う。つまり DrawSetSymbol() は dbuf という変数に座標と一緒に
  191.         buf のアドレスを書き込んでいるに過ぎません。buf の中身を参照
  192.         するのはずっとあとになってからです。だから buf の同一アドレス
  193.         が2度書き込まれ、2度目の sprintf() で書き換えた内容のみあと
  194.         から参照されてしまうわけです。
  195.  
  196.     2) auto 変数領域のアドレスを呼び出し元へ返している
  197.  
  198.         スタック上に確保された文字列バッファ buf のアドレスを、呼び出
  199.         し元に返しています。リターンとともにスタックフレームは消滅し
  200.         ますので、これではそのスタックに別の値が書き込まれてもおかし
  201.         くはありません。
  202.  
  203.  
  204.     というわけで、正しくは(もしこのまま修正するなら)次のようになります。
  205.  
  206.         drawset( dbuf, num1, num2 )
  207.         DrawBuf    *dbuf;
  208.         {
  209.             static char    buf1[100],
  210.                     buf2[100];
  211.             sprintf( buf, "%d", num1 );
  212.             DrawSetSymbol( dbuf,   X1, Y1, buf1, ATTR, FONT );
  213.             sprintf( buf, "%d", num2 );
  214.             DrawSetSymbol( dbuf+1, X2, Y2, buf2, ATTR, FONT );
  215.         }
  216.  
  217.  
  218.  
  219. --
  220. 1994 9/01 作成
  221. 1995 9/15 加筆修正
  222.  
  223. 小笠原博之
  224. oga@dgw.yz.yamagata-u.ac.jp
  225. DenDenNET: DEN0006 COR.
  226.  
  227.